#ifndef LIST_H_
#define LIST_H_

/*
Copyright (C) 2011 Salil Bhagurkar

This file is part of illusion

illusion is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

illusion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with illusion.  If not, see <http://www.gnu.org/licenses/>.
*/

/*Doubly linked list management macros *with a tail**/
/*Declare a list as struct whatever *whatever, *whatevertail */

#define list_for(list_head,node) for(node=list_head;node;node=node->next)

#define list_reverse_for(list_tail, node) for(node = list_tail; node; node = node->prev)

/*
..--><--before--><--...
node-->
*/

#define list_insert_before(list_head,list_tail,before,node) ({\
	if(!list_head) {\
		list_head=node;\
		list_tail=node;\
		node->next=null;\
		node->prev=null;\
	} else {\
		if(before->prev)\
			(before->prev)->next=node;\
		node->prev=before->prev;\
		before->prev=node;\
		node->next=before;\
		if(list_head==before)\
			list_head=node;\
	}\
})


#define list_insert_after(list_head,list_tail,after,node) ({\
	if(!list_head) {\
		list_head=node;\
		list_tail=node;\
		node->next=null;\
		node->prev=null;\
	} else {\
		if(after->next)\
			(after->next)->prev=node;\
		node->next=after->next;\
		after->next=node;\
		node->prev=after;\
		if(list_tail==after)\
			list_tail=node;\
	}\
})

#define list_attach_head(list_head,list_tail,node) ({\
	if(!list_tail) {\
		list_head=node;\
		list_tail=node;\
		node->next=null;\
	} else {\
		list_head->prev=node;\
		node->next=list_head;\
		list_head=node;\
	}\
	node->prev=null;\
})

#define list_attach(list_head,list_tail,node) ({\
	if(!list_tail) {\
		list_head=node;\
		list_tail=node;\
		node->prev=null;\
	} else {\
		list_tail->next=node;\
		node->prev=list_tail;\
		list_tail=node;\
	}\
	node->next=null;\
})

/*
 * Replaces a node in a list with a new node
 */
#define list_replace(list_head, list_tail, node, newnode) ({\
	if(node->prev) {\
		newnode->prev = node->prev;\
		(node->prev)->next = newnode;\
		node->prev = null;\
	}\
	if(node->next) {\
		newnode->next = node->next;\
		(node->next)->prev = newnode;\
		node->next = null;\
	}\
})

#define list_detach_head(list_head,list_tail,node) ({\
	if(!list_tail) {\
		list_head=node;\
		list_tail=node;\
		node->next=null;\
	} else {\
		list_head->prev = node;\
		node->next = list_head;\
		list_head = node;\
	}\
	node->prev=null;\
})

#define list_detach(list_head,list_tail,node) ({\
	if(list_head==node && list_tail==node)\
		list_head=list_tail=null;\
	else {\
		if(!(node->prev)) {\
			list_head=node->next;\
			list_head->prev=null;\
		} else if(!(node->next)) {\
			list_tail=node->prev;\
			list_tail->next=null;\
		} else {\
			(node->prev)->next=node->next;\
			(node->next)->prev=node->prev;\
		}\
	}\
	node->next = null;\
	node->prev = null;\
})

#define list_is_empty(list_head) (list_head)

#define is_in_list(list_head,node) ( (list_head) && ((node)==(list_head) || (node)->next || (node)->prev) )

#define is_not_in_list(list_head,node) ( list_head!=node && node->prev == null && node->next == null)

#define list_init_node(node) node->next = node->prev = null

/*Initialize next and prev statically*/
#define list_init_node_decl .next=null,.prev=null


#endif /* LIST_H_ */
